from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtCharts import *
from typing import List
from color_palette import *

CLOCK_HEIGHT = 48
CLOCK_WIDTH = 256
CIRCLE_MARGIN = 2

def open_file_dialog(title:str, file_type:str):
    file_name, _ = QFileDialog.getOpenFileName(None, title, "", "*."+file_type)
    return file_name

def open_chart_dialog(data, legends:List[str], title:str, x_label:str, y_label:str):
    ChartDialog().show_chart(data, legends, title, x_label, y_label)

def save_file_dialog(title:str, file_type:str):
    file_name, _ = QFileDialog.getSaveFileName(None, title, "", "*."+file_type)
    return file_name

def open_progress_dialog(title:str,  max_value:int) -> QProgressDialog:
    progress_dialog = QProgressDialog("进度", "取消", 0, max_value)
    progress_dialog.setWindowModality(Qt.WindowModality.ApplicationModal)
    progress_dialog.setWindowTitle(title)
    progress_dialog.setMinimumDuration(2000)
    progress_dialog.setWindowIcon(QIcon('icon/tip.svg'))
    return progress_dialog

def get_styled_table_widget() -> QTableWidget:
    table_widget = QTableWidget()
    table_widget.setFrameStyle(QFrame.Shape.NoFrame)
    table_widget.setGridStyle(Qt.PenStyle.DashLine)
    table_widget.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
    table_widget.setSelectionMode(QTableWidget.SelectionMode.SingleSelection)
    table_widget.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
    table_widget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
    table_widget.verticalHeader().setHidden(True)
    table_widget.setFocusPolicy(Qt.FocusPolicy.NoFocus)
    return table_widget

def get_spacer_item(expand_h = True, expand_v = True) -> QSpacerItem:
    h_size_policy = QSizePolicy.Policy.Expanding if expand_h else QSizePolicy.Policy.Fixed
    v_size_policy = QSizePolicy.Policy.Expanding if expand_v else QSizePolicy.Policy.Fixed
    return QSpacerItem(10, 10, h_size_policy, v_size_policy)

def get_shadow_effect() -> QGraphicsDropShadowEffect:
    shadow = QGraphicsDropShadowEffect()
    shadow.setBlurRadius(10)
    shadow.setColor(QColor(0, 0, 0, 50))
    shadow.setOffset(0, 0)
    return shadow

def get_html_table(data, header:List[str] = [], cellpadding:int = 4, align:str = "center")->str:
    info = f"<table cellspacing=\"0\" border = \"0\" cellpadding=\"{cellpadding}\">"

    if len(header) != 0:
        info += '<tr>'
        for h in header:
            info += f'<th align=\"{align}\">' + h + '</th>'
        info += '</tr>'
    
    for row in data:
        info += '<tr>'
        for item in row:
            info += '<td align=\"{align}\" >' + item + '</td>'
        info += '</tr>'
    info += '</table>'
    return info

class DataLoadingThread(QThread):
    loading_complete_signal = pyqtSignal(bool, str)
    def __init__(self) -> None:
        super().__init__()
        self.loading_func = None
    
    def run(self) -> None:
        success, message = False, "出错啦"
        if self.loading_func:
            success, message = self.loading_func()
        self.loading_complete_signal.emit(success, message)

class DataSavingThread(QThread):
    def __init__(self) -> None:
        super().__init__()
        self.saving_func = None
    
    def run(self) -> None:
        if self.saving_func:
            try:
                self.saving_func()
            except Exception as e:
                print(e)
                return

class ClockWidget(QWidget):
    def __init__(self) -> None:
        super().__init__()
        clock = QLCDNumber(8)
        clock.setFixedSize(CLOCK_WIDTH ,CLOCK_HEIGHT)
        clock.setSegmentStyle(QLCDNumber.SegmentStyle.Flat)
        clock.display("00:00:00")
        self.clock = clock

        date_label = QLabel()
        date_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        date_label.setText("2000年01月01日")
        self.date_label = date_label

        layout = QGridLayout()
        layout.addItem(get_spacer_item(), 0, 0, 2)
        layout.addWidget(clock, 0, 1)
        layout.addWidget(date_label, 1, 1)
        layout.addItem(get_spacer_item(), 0, 2, 2)
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)
        self.setFixedWidth(CLOCK_WIDTH) 

    def update_date_and_time(self, date:str, time:str):
        self.date_label.setText(date)
        self.clock.display(time)

class CircleProgeressWidget(QWidget):
    def __init__(self, radius:int) -> None:
        super().__init__()
        self.progress_value = 1
        self.color = Qt.GlobalColor.cyan
        self.radius = radius
        self.setFixedSize(radius * 2 + CIRCLE_MARGIN, radius * 2 + CIRCLE_MARGIN)
        self.setContentsMargins(CIRCLE_MARGIN, CIRCLE_MARGIN, CIRCLE_MARGIN, CIRCLE_MARGIN)

    def update_content(self, color:QColor, progress_value:float):
        if self.color == color and self.progress_value == progress_value or progress_value < 0:
            return
        self.color = color
        self.progress_value = progress_value
        self.update()
    
    def paintEvent(self, event:QPaintEvent) -> None:
        rect = self.contentsRect()
        painter = QPainter(self)
        painter.setRenderHints(QPainter.RenderHint.Antialiasing)

        painter.setPen(VEHICLE_UNCOMPLETE_COLOR)
        painter.setBrush(VEHICLE_UNCOMPLETE_COLOR)
        painter.drawEllipse(rect)

        start_angle = 90 * 16
        span_angle = int(360 * 16 * self.progress_value)
        painter.setPen(self.color)
        painter.setBrush(self.color)
        painter.drawPie(rect, start_angle, span_angle)
    
class TipDialog(QDialog):
    def __init__(self) -> None:
        super().__init__()
        self.tip_label = QLabel("")
        layout = QHBoxLayout()
        layout.addWidget(self.tip_label)
        self.setLayout(layout)
        self.setWindowTitle("提示")
        self.setWindowIcon(QIcon('icon/tip.svg'))
    
    def show_tip(self, tip:str):
        self.tip_label.setText(tip)
        self.show()

class ModalTipDialog(TipDialog):
    def __init__(self) -> None:
        super().__init__()
        self.setModal(True)
        self.setWindowFlag(Qt.WindowType.WindowCloseButtonHint, False)

class GAParmsDialog(QDialog):
    changed_parms_signal = pyqtSignal(int, int, float, float)
    def __init__(self) -> None:
        super().__init__()
        self.pop_num = 0 
        self.itera_times = 0
        self.cross_rate = 0
        self.mutation_rate = 0

        self.setModal(True)
        layout = QGridLayout()

        self.pop_num_spinbox = QSpinBox()
        self.pop_num_spinbox.setRange(100, 10000)
        
        self.itera_times_spinbox = QSpinBox()
        self.itera_times_spinbox.setRange(100, 10000)

        self.cross_rate_spinbox = QDoubleSpinBox()
        self.cross_rate_spinbox.setRange(0.01, 0.99)
        self.cross_rate_spinbox.setSingleStep(0.01)
        self.cross_rate_spinbox.setDecimals(2)

        self.mutation_rate_spinbox = QDoubleSpinBox()
        self.mutation_rate_spinbox.setRange(0.001, 0.999)
        self.mutation_rate_spinbox.setSingleStep(0.001)
        self.mutation_rate_spinbox.setDecimals(3)

        self.confirm_button = QPushButton("确认")
        self.confirm_button.setFlat(True)
        self.confirm_button.clicked.connect(self.confirm_button_slot)

        self.cancal_button = QPushButton("取消")
        self.cancal_button.setFlat(True)
        self.cancal_button.clicked.connect(lambda:self.hide())

        button_layout = QHBoxLayout()
        button_layout.addWidget(self.confirm_button)
        button_layout.addWidget(self.cancal_button)

        label_alignment = Qt.AlignmentFlag.AlignHCenter|Qt.AlignmentFlag.AlignRight
        spinbox_alignment = Qt.AlignmentFlag.AlignHCenter|Qt.AlignmentFlag.AlignLeft
        layout.addWidget(QLabel("种群大小"), 0, 0, label_alignment)
        layout.addWidget(self.pop_num_spinbox, 0, 1, spinbox_alignment)
        layout.addWidget(QLabel("迭代次数"), 1, 0, label_alignment)
        layout.addWidget(self.itera_times_spinbox, 1, 1, spinbox_alignment)
        layout.addWidget(QLabel("交叉率"), 2, 0, label_alignment)
        layout.addWidget(self.cross_rate_spinbox, 2, 1, spinbox_alignment)
        layout.addWidget(QLabel("变异率"), 3, 0, label_alignment)
        layout.addWidget(self.mutation_rate_spinbox, 3, 1, spinbox_alignment)
        layout.addLayout(button_layout, 4, 0, 1, 2)

        self.setLayout(layout)
        self.setWindowTitle("参数调整")
    
    def show_parms(self, pop_num:int, itera_times:int, cross_rate:float, mutation_rate:float):
        self.pop_num = pop_num
        self.itera_times = itera_times
        self.cross_rate = cross_rate
        self.mutation_rate = mutation_rate

        self.pop_num_spinbox.setValue(pop_num)
        self.itera_times_spinbox.setValue(itera_times)
        self.cross_rate_spinbox.setValue(cross_rate)
        self.mutation_rate_spinbox.setValue(mutation_rate)

        self.cancal_button.setFocus()
        self.show()
    
    def confirm_button_slot(self):
        self.hide()
        new_pop_num = self.pop_num_spinbox.value()
        new_itera_times = self.itera_times_spinbox.value()
        new_cross_rate = self.cross_rate_spinbox.value()
        new_mutation_rate = self.mutation_rate_spinbox.value()
        

        if self.pop_num == new_pop_num and \
            self.itera_times == new_itera_times and \
            self.cross_rate == new_cross_rate and \
            self.mutation_rate == new_mutation_rate:
            return

        self.changed_parms_signal.emit(new_pop_num, new_itera_times, new_cross_rate, new_mutation_rate)

class ChartDialog(QDialog):
    def __init__(self) -> None:
        super().__init__()

        chart = QChart()
        chart.setBackgroundRoundness(0)
        chart.layout().setContentsMargins(0, 0, 0, 0)
        chart.legend().setMarkerShape(QLegend.MarkerShape.MarkerShapeCircle)
        self.chart = chart

        chart_view = QChartView()
        chart_view.setChart(chart)
        chart_view.setRenderHint(QPainter.RenderHint.Antialiasing, True)
        chart_view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
        self.chart_view = chart_view

        layout = QHBoxLayout()
        layout.addWidget(chart_view)
        self.setLayout(layout)
    
    def show_chart(self, data, legends:List[str], title:str, x_label:str, y_label:str):
        for i, list in enumerate(data):
            series = QLineSeries(self.chart)
            for x, y in enumerate(list):
                series.append(x, y)
            series.setName(legends[i])
            self.chart.addSeries(series)

        if len(legends) < 2:
            self.chart.legend().hide()
        else:
            self.chart.legend().show()

        self.chart.createDefaultAxes()

        axes = self.chart.axes()
        axes[0].setTitleText(x_label)
        axes[1].setTitleText(y_label)
        axes[1].setLabelFormat('%.3e')

        self.chart.setTitle(title)
        self.setWindowTitle(title)
        self.exec()
    